<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=565541
-->
<window title="Mozilla Bug 565541"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>

  <!-- test results are displayed in the html:body -->
  <body xmlns="http://www.w3.org/1999/xhtml">
  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=565541"
     target="_blank">Mozilla Bug 565541</a>
  </body>

  <!-- test code goes here -->
  <script type="application/javascript">
  <![CDATA[

  /** Test for Bug 565541 **/
  var previousX, previousY, previousWidth, previousHeight;

  function backValues()
  {
    previousX = window.top.screenX;
    previousY = window.top.screenY;
    previousWidth = window.top.innerWidth;
    previousHeight = window.top.innerHeight;
  }

  function restoreValues()
  {
    window.top.screenX = previousX;
    window.top.screenY = previousY;
    window.top.innerWidth = previousWidth;
    window.top.innerHeight = previousHeight;
  }

  function getNewWidth(aWindow)
  {
    return (aWindow.innerWidth > (screen.width / 2)) ? 100 : screen.width;
  }

  function getNewHeight(aWindow)
  {
    return (aWindow.innerHeight > (screen.height / 2)) ? 100 : screen.height;
  }

  function getNewX(aWindow)
  {
    return (aWindow.screenX > ((screen.width - aWindow.outerWidth) / 2))
      ? 0 : screen.width - aWindow.outerWidth;
  }

  function getNewY(aWindow)
  {
    return (aWindow.screenY > ((screen.height - aWindow.outerHeight) / 2))
      ? 0 : screen.height - aWindow.outerHeight;
  }

  /**
   * hitEventLoop is called when we want to check something but we can't rely on
   * an event or a specific number of event loop hiting.
   * This method can be called by specifying a condition, a test (using SimpleTest
   * API), how many times the event loop has to be hitten and what to call next.
   * If times < 0, the event loop will be hitten as long as the condition isn't
   * true or the test doesn't time out.
   */
  function hitEventLoop(condition, test, times, next) {
    if (condition() || times == 0) {
      test();
      next();
      return;
    }

    setTimeout(hitEventLoop, 0, condition, test, times - 1, next);
  }

  function checkChangeIsEnabled(aWindow, aNext)
  {
    // Something should happen. We are not going to go to the next test until
    // it does.
    var hits = -1;

    var prevWidth;
    var prevHeight;

    var prevX;
    var prevY;

    var oWidth;
    var oHeight;

    function sizeChangeCondition() {
      return aWindow.innerWidth != prevWidth && aWindow.innerHeight != prevHeight;
    }

    function sizeChangeTest() {
      isnot(aWindow.innerWidth, prevWidth, "Window width should have changed");
      isnot(aWindow.innerHeight, prevHeight, "Window height should have changed");

      prevWidth = aWindow.innerWidth;
      prevHeight = aWindow.innerHeight;
    }

    function posChangeCondition() {
      // With GTK, sometimes, only one dimension changes.
      if (navigator.platform.indexOf('Linux') != -1) {
        return aWindow.screenX != prevX || aWindow.screenY != prevY;
      }
      return aWindow.screenX != prevX && aWindow.screenY != prevY;
    }

    function posChangeConditionIgnoreLinux() {
      if (posChangeCondition()) {
        return true;
      }

      if (navigator.platform.indexOf('Linux') != -1) {
        return true;
      }
    }

    function posChangeTest() {
      // With GTK, sometimes, only one dimension changes.
      if (navigator.platform.indexOf('Linux') != -1) {
        // With GTK, sometimes, aWindow.screenX changes during two calls.
        // So we call it once and save the returned value.
        var x = aWindow.screenX;
        var y = aWindow.screenY;
        if (x != prevX) {
          isnot(x, prevX, "Window x position should have changed");
        }
        if (y != prevY) {
          isnot(y, prevY, "Window y position should have changed");
        }
      } else {
        isnot(aWindow.screenX, prevX, "Window x position should have changed");
        isnot(aWindow.screenY, prevY, "Window y position should have changed");
      }

      prevX = aWindow.screenX;
      prevY = aWindow.screenY;
    }

    function outerChangeCondition() {
      return aWindow.outerWidth != oWidth && aWindow.outerHeight != oHeight;
    }

    function outerChangeTest() {
      isnot(aWindow.outerWidth, oWidth, "Window outerWidth should have changed");
      isnot(aWindow.outerHeight, oHeight, "Window outerHeight should have changed");

      aWindow.outerWidth = oWidth;
      aWindow.outerHeight = oHeight;
    }

    /**
     * Size checks.
     */
    prevWidth = aWindow.innerWidth;
    prevHeight = aWindow.innerHeight;

    aWindow.innerWidth = getNewWidth(aWindow);
    aWindow.innerHeight = getNewHeight(aWindow);

    hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
      aWindow.resizeTo(getNewWidth(aWindow), getNewHeight(aWindow));

    hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
      aWindow.resizeBy(getNewWidth(aWindow) - aWindow.innerWidth,
                       getNewHeight(aWindow) - aWindow.innerHeight);

    hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
      prevWidth = aWindow.innerWidth = getNewWidth(aWindow);
      prevHeight = aWindow.innerHeight = getNewHeight(aWindow);
      aWindow.sizeToContent();

    hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
    /**
     * Position checks.
     */
      prevX = aWindow.screenX;
      prevY = aWindow.screenY;

      aWindow.screenX = getNewX(aWindow);
      aWindow.screenY = getNewY(aWindow);

    hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
      aWindow.moveTo(getNewX(aWindow), getNewY(aWindow));

    hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
      aWindow.moveBy(getNewX(aWindow) - aWindow.screenX,
                     getNewY(aWindow) - aWindow.screenY);

    hitEventLoop(posChangeConditionIgnoreLinux, posChangeTest, hits, function () {
    /**
     * Outer width/height checks.
     */
      oWidth = aWindow.outerWidth;
      oHeight = aWindow.outerHeight;

      aWindow.outerWidth = oWidth * 2;
      aWindow.outerHeight = oHeight * 2;

    hitEventLoop(outerChangeCondition, outerChangeTest, hits, aNext);
    });
    });
    });
    });
    });
    });
    });
  }

  SimpleTest.waitForExplicitFinish();
  SimpleTest.waitForFocus(function() {
    if (screen.width <= 200 || screen.height <= 200) {
      todo(false, "The screen is too small to run this test.");
      SimpleTest.finish();
    }

    backValues();

    // We are in a chrome context, we can change the size and position.
    checkChangeIsEnabled(window.top, function() {
      // We create a window and check that the size and position can be set with
      // window.open parameters and can be changed by the created window.
      var w = window.open("data:text/html,<script>" +
        "function check(next) {" +
        "  var is_range = function(aTest, aValue, aRange, aMsg) {" +
        "    ok(aTest < aValue + aRange && aTest > aValue - aRange, aMsg);" +
        "  };" +
        "  is_range(window.innerWidth, 170, 5, 'parameter width should be taken into account');" +
        "  is_range(window.innerHeight, 170, 5, 'parameter height should be taken into account');" +
        "  is_range(window.screenX, 25, 5, 'parameter screenX should be taken into account');" +
        "  is_range(window.screenY, 25, 5, 'parameter screenY should be taken into account');" +
        "  checkChangeIsEnabled(window, next);" +
        "} <\/script>", '',
        'width=170,height=170,screenX=25,screenY=25');

      SimpleTest.waitForFocus(function() {
        // This test relies on passing cross-origin windows to a chrome function,
        // which is generally forbidden if we don't twiddle the knob below.
        Components.utils.forcePermissiveCOWs();
        w.wrappedJSObject.ok = SimpleTest.ok;
        w.wrappedJSObject.checkChangeIsEnabled = window.checkChangeIsEnabled;
        w.wrappedJSObject.check(function() {
          // The current window can change the size and position of the created one.
          checkChangeIsEnabled(w, function() {
            w.close();

            // If we call window.open with an empty string as a third parameter,
            // by default, it will create a new tab instead of a new window.
            // In a chrome context, the size and position can change.
            w = window.open("data:text/html,<script>" +
              "function check(next) {" +
              "  checkChangeIsEnabled(window, next);" +
              "} <\/script>", '', '');

            SimpleTest.waitForFocus(function() {
               w.wrappedJSObject.checkChangeIsEnabled = window.checkChangeIsEnabled;
               w.wrappedJSObject.check(function() {
                // The current window can change the size and position of the new tab.
                // Because we are in a chrome context.
                checkChangeIsEnabled(w, function() {
                  w.close();

                  restoreValues();
                  SimpleTest.finish();
                });
              });
            }, w, false);
          });
        });
      }, w, false);
    });
  });
  ]]>
  </script>
</window>
